package tx.core;

import java.awt.Graphics2D;

/**
 * A tetraminoe: a set of 4 blocks.
 * @author Guilherme Mauro Germoglio Barbosa - germoglio@gmail.com
 */
public abstract class Tetraminoe {

	/**
	 * The obvious number of blocks of a Tetraminoe.
	 */
	public static final int NUMBER_OF_BLOCKS = 4;

	/**
	 * The X speed for moving.
	 */
	public static final float X_SPEED = .3f;
	
	/**
	 * The for blocks of a Tetraminoe.
	 */
	private final Block[] blocks;

	/**
	 * If the tetraminoe is going down accelerated or not.
	 */
	private boolean isGoingDown;

	/**
	 * The relative coordinates that indicate each position for each block in each rotation state.
	 */
	private final int[][][] relativeCoordinates;

	/**
	 * The offsets used for correcting positions after rotation.
	 */
	private final int[][] rotationOffsets;

	/**
	 * The orientation of rotation.
	 */
	private int orientation;

	/**
	 * Creates a Tetraminoe using its color, relative coordinates (for each rotation state) and its rotation offsets
	 * (used for correcting coordinates after rotation).
	 * @param color The color of the blocks.
	 * @param relativeCoordinates The relative coordinates that indicate each position for each block in each rotation state.
	 * @param rotationOffsets The offsets used for correcting positions after rotation.
	 */
	public Tetraminoe(int color, int[][][] relativeCoordinates, int[][] rotationOffsets) {
		
		this.relativeCoordinates = relativeCoordinates;
		this.rotationOffsets = rotationOffsets;
		this.orientation = 0;
		
		this.blocks = createBlocks(color);
		
		this.setX(0);
		this.setX(0);
	}

	/**
	 * Creates the blocks for a Tetraminoe with the corresponding color.
	 * @param color The color. It can be found in tx.core.BlockColors constants.
	 * @return The Blocks of a Tetraminoe with the corresponding color. 
	 */
	private Block[] createBlocks(int color) {
		Block[] blocks = new Block[NUMBER_OF_BLOCKS];
		
		for (int k = 0; k < NUMBER_OF_BLOCKS; k++) {
			blocks[k] = Block.createBlock(color);
		}
		
		return blocks;
	}
	
	/**
	 * Updates all blocks.
	 * @param elapsedTime The elapsed time.
	 */
	public void update(long elapsedTime) {
		for (Block block : blocks) {
			block.update(elapsedTime);
		}
	}
	
	/**
	 * Draws the tetraminoe.
	 * @param g The Graphics2D object to do it.
	 */
	public void draw(Graphics2D g) {
		for (Block block : blocks) {
			g.drawImage(block.getImage(), Math.round(block.getX()), Math.round(block.getY()), null);
		}
	}
	
	/**
	 * Rotates the tetraminoe.
	 */
	public void rotate() {
		
		int w = blocks[0].getWidth();
		int h = blocks[0].getHeight();
		
		float offsetX = 0;
		float offsetY = 0;
		
		int index = orientation % relativeCoordinates.length;

		offsetX =  this.rotationOffsets[index][0] * w;
		offsetY =  this.rotationOffsets[index][1] * h;
		
		orientation++;
		
		setX(offsetX + blocks[0].getX());
		setY(offsetY + blocks[0].getY());
	}
	
	/**
	 * Returns the blocks.
	 * @return The blocks of this Tetraminoe.
	 */
	public Block[] getBlocks() {
		return this.blocks;
	}
	
	/**
	 * Sets a new x for the tetraminoe.
	 * @param x A new x for the tetraminoe.
	 */
	public void setX(float x) {
		for (int k = 0; k < NUMBER_OF_BLOCKS; k++) {
			blocks[k].setX(x + (relativeCoordinates[orientation%relativeCoordinates.length][k][0] * blocks[k].getWidth()));
		}
	}
	
	/**
	 * Sets a new y for the tetraminoe.
	 * @param y A new y for the tetraminoe.
	 */
	public void setY(float y) {
		for (int k = 0; k < NUMBER_OF_BLOCKS; k++) {
			blocks[k].setY(y + (relativeCoordinates[orientation%relativeCoordinates.length][k][1] * blocks[k].getWidth()));
		}
	}

	/**
	 * Sets the new y velocity for all blocks.
	 * @param dy The new y velocity.
	 */
	public void setVelocityY(float dy) {
		for (Block block : blocks) {
			block.setVelocityY(dy);
		}
	}

	/**
	 * Sets the new x velocity for all blocks.
	 * @param dx The new x velocity.
	 */
	public void setVelocityX(float dx) {
		for (Block block : blocks) {
			block.setVelocityX(dx);
		}
	}

	/**
	 * If the tetraminoe is going down and accelerating.
	 * @return <code>true</code> if so, <code>false</code> otherwise.
	 */
	public boolean isGoingDown() {
		return isGoingDown;
	}

	/**
	 * Makes the tetraminoe goes down, and accelerating.
	 */
	public void goDown() {
		this.isGoingDown = true;
		for (Block block : blocks) {
			block.goDown();
		}
	}
}
